import numpy as np
from mcts.base.base import BaseState,BaseAction
from mcts.searcher.mcts import MCTS
from copy import deepcopy
import time
import logging

logging.basicConfig(level=logging.INFO)

# actionList=['积点','一枪','两枪','三枪','四枪','大炮', #0~5
#             '穿刺','核武器','超级核武器','刺客','趴下','反弹', #6~11
#             '吸收','强制吸收','牺牲','回血','自爆','交易','克隆', #12~18
#             '中分','三分头','全体炸弹','每人两枪','火箭','石炸天', #19~24
#             '矿工'] #25

cost=np.array([[-1,1,2,3,4,5,6,10,20,2,0,0,2,4,-3,4 ,2,0,1,2,3,4,4,6,8,5],
               [0 ,0,0,0,0,0,0,0 ,0 ,0,0,0,0,0,1 ,-1,1,1,0,0,0,0,0,0,0,0]])

players=3
_lives=[]
for i in range(players):
    _lives.append(2)

#单人攻击技能指定toplayer
#群体无差别攻击技能指定toplayer=player数量
#群体指定人攻击技能指定toplayer为要攻击的player list
#防御技能指定toplayer=-1

attacks=[1,2,3,4,5,6,8,9,12,13]#单人攻击
defends=[10,11,14,15,16,17,18]#并非攻击
multiattacks1=[7,21,23,24,25]#群体无差别攻击
multiattacks2=[19,20,22]#群体指定人攻击

liveplayers=[]
deadplayers=[]

def getPossibleLivePlayers(fromplayer,num)->list[list or int]:
    global liveplayers
    if num==1:
        _liveplayers=deepcopy(liveplayers)
        if fromplayer in _liveplayers:
            _liveplayers.remove(fromplayer)
        return _liveplayers
    res=[]
    for i in range(len(liveplayers)):
        player=liveplayers[i]
        if player==fromplayer:
            continue
        for _players in getPossibleLivePlayers(fromplayer,num-1):
            if isinstance(_players,int):
                if player<_players:
                    res.append([player,_players])
            else:
                _player=_players[0]
                _res=[player]
                if player<_player:
                    _res.extend(_players)
                    res.append(_res)
    return res

class Action(BaseAction):
    def __init__(self,actionType,fromplayer,toplayer:int or list=-1,manual=False):
        self.fromplayer=fromplayer
        self.toplayer=toplayer
        self.actionType=actionType
        self.manual=manual

    def __str__(self):
        return '%s from %s to %s'%(self.actionType,self.fromplayer,self.toplayer)

    def __repr__(self):
        return str(self)

    def __eq__(self,other):
        return self.__class__==other.__class__ and self.actionType==other.actionType \
               and self.fromplayer==other.fromplayer and self.toplayer==other.toplayer

    def __hash__(self):
        return hash((self.actionType,self.fromplayer,str(self.toplayer),self.manual))

class MultiJiDian(BaseState):
    def __init__(self):
        self.currentPlayer: int=0
        self.round: int=0
        self.points=np.zeros(players,dtype=int)
        self.lives=np.array(_lives)
        self.attacks=np.zeros(players,dtype=int)
        self.defends=np.zeros(players,dtype=int)
        self.trade=np.zeros([2,players],dtype=int)

        self.tempPoints=np.zeros(players,dtype=int)
        self.tempLives=np.array(_lives)
        self.tempAttacks=np.zeros(players,dtype=int)
        self.tempDefends=np.zeros(players,dtype=int)
        self.tempTypes=np.zeros(players,dtype=int)
        self.tempTrade=np.zeros([2,players],dtype=int)

        self.fromplayers=[]
        self.toplayers=[]

    def get_current_player(self) -> int:
        return self.currentPlayer

    def get_possible_actions(self):
        fromplayer=self.currentPlayer
        possibleActions=[Action(actionType=0,fromplayer=fromplayer)]
        for toplayer in range(players):
            if fromplayer==toplayer:
                continue
            if self.lives[toplayer]<=0:
                continue
            for i in attacks:
                if cost[0,i]<=self.points[fromplayer]:
                    possibleActions.append(Action(actionType=i,fromplayer=fromplayer,toplayer=toplayer))
        for i in defends:
            if sum(self.points)-self.points[fromplayer]==0:
                if 10<=i<=11 or i==18:
                    continue
            if cost[0,i]<=self.points[fromplayer] and cost[1,i]<self.lives[fromplayer]:
                possibleActions.append(Action(actionType=i,fromplayer=fromplayer))
        for i in multiattacks1:
            if cost[0,i]<=self.points[fromplayer]:
                possibleActions.append(Action(actionType=i,fromplayer=fromplayer,toplayer=players))
        for i in multiattacks2:
            if cost[0,i]<=self.points[fromplayer]:
                if i==19:
                    for _liveplayers in getPossibleLivePlayers(fromplayer,2):
                        if _liveplayers:
                            possibleActions.append(Action(actionType=i,fromplayer=fromplayer,toplayer=_liveplayers))
                elif i==20:
                    for _liveplayers in getPossibleLivePlayers(fromplayer,3):
                        if _liveplayers:
                            possibleActions.append(Action(actionType=i,fromplayer=fromplayer,toplayer=_liveplayers))
                elif i==22:
                    for _liveplayers in getPossibleLivePlayers(fromplayer,2):
                        if _liveplayers:
                            possibleActions.append(Action(actionType=i,fromplayer=fromplayer,toplayer=_liveplayers))

        return possibleActions

    def toTemp(self):
        for player in range(players):
            self.tempPoints[player]=deepcopy(self.points[player])
            self.tempLives[player]=deepcopy(self.lives[player])
            self.tempAttacks[player]=deepcopy(self.attacks[player])
            self.tempDefends[player]=deepcopy(self.defends[player])
        self.tempTrade[0]=deepcopy(self.trade[0])
        self.tempTrade[1]=deepcopy(self.trade[1])

    def fromTemp(self):
        for player in range(players):
            self.points[player]=deepcopy(self.tempPoints[player])
            self.lives[player]=deepcopy(self.tempLives[player])
            self.attacks[player]=deepcopy(self.tempAttacks[player])
            self.defends[player]=deepcopy(self.tempDefends[player])
        self.trade[0]=deepcopy(self.tempTrade[0])
        self.trade[1]=deepcopy(self.tempTrade[1])

    def is_terminal(self):
        #if self.currentPlayer==0:
        #    return False
        liveplayers=[]
        for player in range(players):
            if self.lives[player]>0:
                liveplayers.append(player)
        if len(liveplayers)==1:
            return True
        return False

    def get_reward(self):
        return self.lives[self.currentPlayer]

    def actionResearch(self,fromplayer,toplayer):
        coef=np.power(2,self.tempTrade[0,fromplayer])
        _coef=np.power(2,self.tempTrade[0,toplayer])  #交易导致的对方防御倍率
        myMove=self.tempTypes[fromplayer]  #自己的action
        otherMove=self.tempTypes[toplayer]  #对方的action
        if myMove==6:  #己方穿刺
            if self.tempAttacks[fromplayer]>=3:  #遭到三枪以上伤害，穿刺无效
                self.tempLives[toplayer]+=1
        if otherMove==6:  #对方穿刺
            if self.tempAttacks[toplayer]<=2:
                self.tempAttacks[fromplayer]+=myMove*coef
                self.tempDefends[fromplayer]=0
                self.tempDefends[toplayer]=self.tempAttacks[toplayer]
        if otherMove==10 and myMove!=7 and myMove!=8:
            if myMove==0:
                self.tempDefends[fromplayer]=10
            elif myMove==9:
                self.tempLives[toplayer]=0
            else:
                self.tempDefends[toplayer]=3*_coef
        if otherMove==11:
            if self.tempAttacks[toplayer]<=2:  #一枪或两枪，反弹
                self.tempAttacks[fromplayer]+=myMove*coef
                self.tempDefends[fromplayer]=0
                self.tempDefends[toplayer]=self.tempAttacks[toplayer]
        if otherMove==12:  #吸收
            if self.tempAttacks[toplayer]<=9*_coef:
                self.tempPoints[toplayer]+=self.tempAttacks[toplayer]
                self.tempDefends[toplayer]=self.tempAttacks[toplayer]
        if otherMove==13:  #强制吸收
            if self.tempAttacks[toplayer]<=19*_coef:
                self.tempPoints[toplayer]+=self.tempAttacks[toplayer]
                self.tempDefends[toplayer]=self.tempAttacks[toplayer]
            if myMove==0:
                self.tempPoints[fromplayer]-=1
                self.tempPoints[toplayer]+=1
        if otherMove==16:  #自爆
            if self.tempAttacks[toplayer]>0:
                self.tempLives[fromplayer]=0
            else:
                self.tempLives[toplayer]=0
        if otherMove==18:  #克隆
            if self.tempPoints[toplayer]>=2 and self.tempAttacks[toplayer]>0:
                self.tempDefends[toplayer]=self.tempAttacks[toplayer]
                self.tempAttacks[fromplayer]=self.tempAttacks[toplayer]
                self.tempPoints[toplayer]-=2

    def take_action(self,action:Action):
        newState=deepcopy(self)
        fromplayer=action.fromplayer
        toplayer=action.toplayer

        if fromplayer==-1:
            newState.fromTemp()
            newState.currentPlayer+=1
            self.round+=1
            return newState

        newState.fromplayers.append(fromplayer)
        newState.toplayers.append(toplayer)
        actionType=action.actionType
        newState.toTemp()
        newState.tempTypes[fromplayer]=actionType
        newState.tempPoints[fromplayer]-=cost[0,actionType]
        newState.tempLives[fromplayer]-=cost[1,actionType]

        coef=np.power(2,newState.tempTrade[0,fromplayer])
        if actionType<6:
            newState.tempAttacks[toplayer]+=actionType*coef
            newState.tempDefends[fromplayer]+=actionType*coef
        elif actionType==6:
            newState.tempLives[toplayer]-=coef
        elif actionType==7:
            for player in liveplayers:
                newState.tempAttacks[player]+=5*coef
        elif actionType==8:
            newState.tempAttacks[toplayer]+=20*coef
        elif actionType==9:
            newState.tempAttacks[toplayer]+=coef
        elif actionType==14:
            newState.tempDefends[fromplayer]+=10*coef
        elif actionType==15:
            newState.tempDefends[fromplayer]+=10*coef
        elif actionType==19:
            assert isinstance(toplayer,list)
            for player in toplayer:
                newState.tempAttacks[player]+=coef
            newState.tempDefends[fromplayer]=2
        elif actionType==20:
            assert isinstance(toplayer,list)
            for player in toplayer:
                newState.tempAttacks[player]+=coef
            newState.tempDefends[fromplayer]=3
        elif actionType==21:
            for player in liveplayers:
                newState.tempAttacks[player]+=coef
        elif actionType==22:
            assert isinstance(toplayer,list)
            for player in toplayer:
                newState.tempAttacks[player]+=2*coef
            newState.tempDefends[fromplayer]=4
        elif actionType==23:
            for player in liveplayers:
                newState.tempAttacks[player]+=2*coef
        elif actionType==24:
            for player in liveplayers:
                newState.tempAttacks[player]+=3*coef
        elif actionType==25:
            for player in liveplayers:
                newState.tempAttacks[player]+=coef
            actionType=9

        if fromplayer!=players-1:
            newState.fromTemp()
            newState.currentPlayer+=1
            self.round+=1
            return newState

        for i in range(len(newState.fromplayers)):#从攻击方视角出发，进行技能交互研究
            fromplayer,toplayer=newState.fromplayers[i],newState.toplayers[i]
            if toplayer==-1:
                continue
            if isinstance(toplayer,list):
                for player in toplayer:
                    newState.actionResearch(fromplayer,player)
            elif toplayer==players:
                for player in liveplayers:
                    newState.actionResearch(fromplayer,player)
            else:
                newState.actionResearch(fromplayer,toplayer)
            

        for player in range(players):
            newState.tempLives[player]-=np.maximum(0,newState.tempAttacks[player]-newState.tempDefends[player])
            newState.tempAttacks[player],newState.tempDefends[player]=0,0

            if newState.tempTrade[1,player]>0:  #交易效果结算
                newState.tempTrade[1,player]-=1
            if newState.tempTrade[1,player]==0:
                newState.tempTrade[0,player]=0

        for i in range(len(newState.fromplayers)):
            fromplayer,toplayer=newState.fromplayers[i],newState.toplayers[i]
            if toplayer==-1:
                continue
            if isinstance(toplayer,list):
                for player in toplayer:
                    if newState.tempLives[player]<=0 and player not in deadplayers:
                        newState.tempPoints[fromplayer]+=2
            elif toplayer==players:
                for player in liveplayers:
                    if newState.tempLives[player]<=0 and player not in deadplayers:
                        newState.tempPoints[fromplayer]+=2
            elif newState.tempLives[toplayer]<=0 and toplayer not in deadplayers:
                newState.tempPoints[fromplayer]+=2

        if action.manual:
            #pass
            for player in range(players):
                if player in deadplayers:
                    continue
                print('玩家编号:%s，进行操作:%s，受到攻击:%s，作出防御:%s'%
                      (player,newState.tempTypes[player],newState.tempAttacks[player],newState.tempDefends[player]))

        newState.fromplayers,newState.toplayers=[],[]
        newState.fromTemp()
        newState.currentPlayer=0
        newState.round+=1
        return newState

def interface(rounds,initState):
    global liveplayers,deadplayers
    state=initState
    new_type=np.zeros(players,dtype=int)
    for i in range(rounds):
        points=state.points
        lives=state.lives
        for player in range(players):
            if player in deadplayers:
                continue
            print('玩家%s有点数:%s,命数:%s'%(player,points[player],lives[player]))

        for player in range(players):
            new_type[player]=-1
            if state.lives[player]<=0:
                state=state.take_action(Action(actionType=new_type[player],fromplayer=player,manual=True))
                continue
            toplayer=-1
            while new_type[player]<0:
                new_type[player]=int(input('玩家%s，请输入您要采取的动作'%player))
                while new_type[player]>18:
                    new_type[player]=int(input('玩家%s，请输入您要采取的动作'%player))
                if cost[0,new_type[player]]>points[player]:
                    print('点数不足')
                    new_type[player]=-1
                toplayer=int(input('玩家%s，请输入你要攻击的玩家'%player)) if new_type[player] in attacks else -1
                while player==toplayer:
                    print('对不起，您不能攻击自己！')
                    toplayer=int(input('玩家%s，请输入你要攻击的玩家'%player)) if new_type[player] in attacks else -1
            state=state.take_action(Action(actionType=new_type[player],fromplayer=player,toplayer=toplayer,manual=True))

        # for player in [0,1]:
        #     print('玩家 %s 使用了 %s！'%(player,actionList[new_type[player]]))

        for player in range(players):
            if player in deadplayers:
                continue
            print('玩家 %s 使用了 %s！'%(player,new_type[player]))

        liveplayers=[]
        for player in range(players):
            if state.lives[player]>0:
                liveplayers.append(player)
            elif player not in deadplayers:
                print('玩家%s死了！'%player)
                deadplayers.append(player)
        if len(liveplayers)==1:
            print('玩家%s赢了！'%liveplayers[0])
            return 0

def AIvsAI_interface(rounds,initState):
    global liveplayers,deadplayers
    state=deepcopy(initState)
    new_action=[]
    for i in range(players):
        new_action.append(0)
    for i in range(rounds):
        points=state.points
        lives=state.lives
        for player in range(players):
            if player in deadplayers:
                continue
            print('玩家%s有点数:%s,命数:%s'%(player,points[player],lives[player]))

        for player in range(players):
            if player in deadplayers:
                continue
            state.currentPlayer=player
            searcher=MCTS(timeLimit=12000)
            bestAction,details=searcher.search(initial_state=state,needDetails=True)
            print(details)
            new_action[player]=bestAction
            print('玩家%s思考完毕'%player)

        state.currentPlayer=0

        for player in range(players):
            state=state.take_action(new_action[player])

        # for player in [0,1]:
        #     print('玩家 %s 使用了 %s！'%(player,actionList[new_type[player]]))

        for player in range(players):
            if player in deadplayers:
                continue
            actionType=new_action[player].actionType
            if actionType in attacks:
                toplayer=new_action[player].toplayer
                print('玩家 %s 使用了 %s 攻击了 玩家 %s！'%(player,actionType,toplayer))
            else:
                print('玩家 %s 使用了 %s！'%(player,actionType))
        time.sleep(0.5)

        liveplayers=[]
        for player in range(players):
            if state.lives[player]>0:
                liveplayers.append(player)
            elif player not in deadplayers:
                print('玩家%s死了！'%player)
                deadplayers.append(player)
        if len(liveplayers)==1:
            print('玩家%s赢了！'%liveplayers[0])
            return 0
        elif len(liveplayers)==0:
            print('同归于尽！')
            return 0

initState=MultiJiDian()

if __name__ == '__main__':
    #interface(500,initState)
    AIvsAI_interface(500,initState)